package com.lhl.fw.db.handler;

import com.lhl.fw.core.base.BaseDbEnum;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @className: com.lhl.fw.db.handler.EnumTypeHandler
 * @description:  mybatis枚举类型处理
 * @author: king
 * @date: 2020-12-09 11:11
 **/
@Api(tags = "EnumTypeHandler", produces = "EnumTypeHandler")
@Slf4j
@MappedJdbcTypes(value = {JdbcType.VARCHAR})
public class EnumTypeHandler<E extends Enum<?> & BaseDbEnum<String>> extends BaseTypeHandler<BaseDbEnum<String>> {
    private Class<E> type;

    public EnumTypeHandler() { }

    /**
     * 设置配置文件设置的转换类以及枚举类内容
     * @param type 配置文件中设置的转换类
     */
    public EnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, BaseDbEnum<String> parameter, JdbcType jdbcType) throws SQLException {
        String code = parameter.getDbValue();
        if (jdbcType == null && code != null) {
            ps.setString(i, code);
        } else {
            assert jdbcType != null;
            ps.setObject(i, parameter.getDbValue(), jdbcType.TYPE_CODE); // see r3589
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String i = rs.getString(columnName);
        return rs.wasNull() ? null : getEnum(i);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 根据数据库存储类型决定获取类型，本例子中数据库中存放String类型
        String i = rs.getString(columnIndex);
        return rs.wasNull() ? null : getEnum(i);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 根据数据库存储类型决定获取类型，本例子中数据库中存放String类型
        String i = cs.getString(columnIndex);
        return cs.wasNull() ? null : getEnum(i);
    }

    /**
     * 枚举类型转换，由于构造函数获取了枚举的子类enums
     * @param value 数据库中存储的自定义value属性
     * @return value对应的枚举类
     */
    private E getEnum(String value) {
        log.debug("enum is [{}]", this.type.getName());
        E[] enums = this.type.getEnumConstants();
        if(enums != null) {
            for (E e : enums) {
                if (e.getDbValue().equals(value)) {
                    return e;
                }
            }
        }
        throw new IllegalArgumentException(String.format("未知的枚举类型：%s,请核对%s", value, type.getSimpleName()));
    }
}
